/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Stmt, Type, ArkMethod, ArkFile, ArkClass, ClassSignature, MethodSignature, ArkAssignStmt, FieldSignature, ArkInvokeStmt, methodSubSignatureCompare, FunctionType, FileSignature, NamespaceSignature, GenericType, AbstractBinopExpr } from "arkanalyzer/lib";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Defects, IssueReport } from "../../model/Defects";
import { ClassMatcher, FileMatcher, MethodMatcher, MatcherCallback, MatcherTypes } from "../../matcher/Matchers";
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { CheckerUtils } from "../../utils/checker/CheckerUtils";
const defaultOptions: Options = {
    ignoreStatic: false
}
interface Options {
    /** Whether to skip checking whether `static` methods are correctly bound. */
    ignoreStatic?: boolean;
}
type stmtType = {
    classSignature?: ClassSignature,
    methodSignature?: MethodSignature,
    realGenericTypes: string[]
}
type stmtRightOp = {
    base: {
        constFlag: boolean,
        declaringStmt: Stmt,
        name: string,
        originalValue: string,
        type: stmtType,
        usedStmts: ArkAssignStmt[]
    },
    fieldSignature: FieldSignature
}
type fieldSignature = {
    fieldSignature: FieldSignature
}
type rightOpType = {
    fieldSignature: FieldSignature
}
type declaringSignature = {
    className: string,
    declaringFileSignature: FileSignature,
    declaringNameSpaceSignature: NamespaceSignature
}
type LeftOp = {
    constFlag: boolean,
    declaringStmt: Stmt,
    name: string,
    originalValue: string,
    type: stmtType,
    usedStmts: ArkAssignStmt[]
}
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'UnboundMethodCheck');
const gmetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/unbound-method-check.md", // TODO: support url
    description: "Avoid referencing unbound methods which may cause unintentional scoping of `this`.If your function does not access `this`, you can annotate it with `this: void`, or consider using an arrow function instead. "
};
export class UnboundMethodCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gmetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };
    private classMatcher: ClassMatcher = {
        file: [this.fileMatcher],
        matcherType: MatcherTypes.CLASS
    };
    private methodMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
        class: [this.classMatcher]
    };
    public registerMatchers(): MatcherCallback[] {
        const matchClass: MatcherCallback = {
            matcher: this.fileMatcher,
            callback: this.check
        }
        return [matchClass];
    }
    public check = (file: ArkFile) => {
        // 将规则选项转换为Options类型
        let options: Options;
        if (this.rule && this.rule.option.length > 0) {
            options = this.rule.option[0] as Options;
        } else {
            options = defaultOptions;
        }
        let classNameArray: string[] = [];
        let methodNameArray: string[] = [];
        let ExtendmethodNameArray: string[] = [];
        let fieldNameArray: string[] = [];
        let objectNameArray: string[] = [];
        let objectMethodNameArray: string[] = [];
        const classes = file.getClasses();
        for (const arkClass of classes) {
            const code = arkClass.getCode();
            if (!code) {
                const importInfos = file.getImportInfos();
                if (importInfos?.length > 0) {
                    for (const importInfo of importInfos) {
                        const importClauseName = importInfo.getImportClauseName();
                        const methods = arkClass.getMethods();
                        for (const arkMethod of methods) {
                            const stmts = arkMethod.getBody()?.getCfg()?.getStmts() ?? [];
                            for (const stmt of stmts) {
                                if (!(stmt instanceof ArkAssignStmt)) {
                                    continue;
                                }
                                const text = stmt.getOriginalText();
                                if (!text) {
                                    continue;
                                }
                                const rightOp = stmt.getRightOp() as unknown as stmtRightOp;
                                const leftOp = stmt.getLeftOp() as unknown as LeftOp;
                                const usedStmts = leftOp.usedStmts;
                                if (usedStmts?.length > 0) {
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const leftOp = usedStmt?.getLeftOp()?.toString();
                                        if (leftOp && rightOp.toString() !== importClauseName) {
                                            if (usedStmts?.length > 1) {
                                                if (usedStmts[0] instanceof ArkAssignStmt) {
                                                    const LeftOpName = usedStmts[0].getLeftOp()?.toString();
                                                    this.addIssueReport(file, stmt, text, LeftOpName);
                                                }
                                            } else {
                                                if (text.includes(':') && text.includes(leftOp)) {
                                                    continue;
                                                }
                                                this.addIssueReport(file, stmt, text, leftOp);
                                            }
                                        }
                                    }
                                }
                                const fieldName = rightOp?.fieldSignature?.getFieldName();
                                if (fieldName) {
                                    const target = `${importClauseName}.${fieldName}`;
                                    if (text.includes(target)) {
                                        this.addIssueReport(file, stmt, text, target);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            const classSignature = arkClass.getSignature();
            const className = classSignature.getClassName();
            const category = arkClass.getCategory();
            if (className && !className.startsWith('%')) {
                classNameArray.push(className);
            }
            if (category == 5) {
                const methods = arkClass.getMethods();
                for (const method of methods) {
                    const methodSignature = method.getSignature();
                    const methodSubSignature = methodSignature.getMethodSubSignature();
                    const methodName = methodSubSignature.getMethodName();
                    if (methodName && !methodName.startsWith('%')) {
                        objectMethodNameArray.push(methodName);
                    }
                }
                const declaringArkFile = arkClass.getDeclaringArkFile();
                const defaultClass = declaringArkFile.getDefaultClass();
                const defaultMethod = defaultClass.getDefaultArkMethod();
                const stmts = defaultMethod?.getBody()?.getLocals() ?? [];
                for (const [key, value] of stmts.entries()) {
                    if (key.toString().startsWith('%') || key == 'this') {
                        continue;
                    }
                    const type = value.getType() as unknown as stmtType;
                    const classSignature = type.classSignature;
                    if (classSignature) {
                        const className = value.getName();
                        const classSignatureName = classSignature.getClassName();
                        if (classSignatureName.startsWith('%')) {
                            objectNameArray.push(className);
                        }
                    }
                }
            }
            const methods = arkClass.getMethods();
            const fileds = arkClass.getFields();
            for (const method of methods) {
                const methodSignature = method.getSignature();
                const MethodSubSignature = methodSignature.getMethodSubSignature();
                const methodName = MethodSubSignature.getMethodName();
                if (methodName) {
                    if (!methodName.startsWith('%')) {
                        const staticFlag = MethodSubSignature.isStatic();
                        if (options.ignoreStatic == true && staticFlag == true) {
                            continue;
                        }
                        methodNameArray.push(methodName);
                        const parameters = MethodSubSignature.getParameters();
                        if (parameters.length > 0) {
                            for (const parameter of parameters) {
                                const parameterType = parameter.getType();
                                const parameterName = parameter.getName();
                                const parameterTypeString = parameterType.getTypeString();
                                if (parameterTypeString?.includes('ContainsMethods')) {
                                    const locals = method.getBody()?.getLocals() ?? [];
                                    for (const [key, value] of locals.entries()) {
                                        const valueName = value.getName();
                                        if (valueName == parameterName) {
                                            const usedStmts = value.getUsedStmts();
                                            let leftOpNameArray: string[] = [];
                                            for (const usedStmt of usedStmts) {
                                                if (!(usedStmt instanceof ArkAssignStmt)) {
                                                    continue;
                                                }
                                                const text = usedStmt.getOriginalText() ?? '';
                                                const leftOpName = usedStmt.getLeftOp()?.toString();
                                                if (leftOpNameArray.includes(leftOpName)) {
                                                    continue;
                                                }
                                                leftOpNameArray.push(leftOpName);
                                                const rightOp = usedStmt.getRightOp();
                                                const leftOp = usedStmt.getLeftOp();
                                                if (rightOp instanceof AbstractBinopExpr) {
                                                    const LeftOp = leftOp as unknown as stmtRightOp;
                                                    const baseName = LeftOp?.base?.name;
                                                    const fieldName = LeftOp?.fieldSignature?.getFieldName();
                                                    if (baseName == parameterName && fieldNameArray.includes(fieldName)) {
                                                        this.addIssueReport(file, usedStmt, text, text);
                                                    }
                                                }
                                                else {
                                                    const RightOp = rightOp as unknown as stmtRightOp;
                                                    const baseName = RightOp?.base?.name;
                                                    const fieldName = RightOp?.fieldSignature?.getFieldName();
                                                    if (baseName == parameterName && fieldName) {
                                                        fieldNameArray.push(fieldName);
                                                        this.addIssueReport(file, usedStmt, text, text);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (methodName.startsWith('%')) {
                        const code = method.getCode() ?? '';
                        const regex1 = /\(\) => (.*)/;
                        const match1 = code.match(regex1);
                        const regex2 = /this\s*\.\s*\w+\(\)/;
                        const match2 = code.match(regex2);
                        if (match1 && !match2) {
                            for (const arkField of fileds) {
                                const fieldSignature = arkField.getSignature();
                                const fieldName = fieldSignature.getFieldName();
                                if (fieldName) {
                                    fieldNameArray.push(fieldName);
                                }
                            }
                        }
                        if (match1 && match2) {
                            const match2Code = match2[0];
                            const regex3 = /this\.(\w+)\(\)/;
                            const match3 = match2Code.match(regex3);
                            if (match3) {
                                const match3Code = match3[1];
                                if (methodNameArray.includes(match3Code)) {
                                    for (const arkField of fileds) {
                                        const fieldSignature = arkField.getSignature();
                                        const fieldName = fieldSignature.getFieldName();
                                        if (fieldName) {
                                            fieldNameArray.push(fieldName);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            const declaringArkFile = arkClass.getDeclaringArkFile();
            const classes = declaringArkFile.getClasses();
            if (fieldNameArray.length == 0) {
                for (const Class of classes) {
                    const ClassSignature = Class.getSignature();
                    const ClassName = ClassSignature.getClassName();
                    const extendedClasses = Class.getExtendedClasses();
                    for (const [key, value] of extendedClasses.entries()) {
                        if (!key || !value) {
                            continue;
                        }
                        const methods = value.getMethods();
                        if (methods && methods.length > 0) {
                            for (const method of methods) {
                                const extendMethodName = method.getSignature().getMethodSubSignature().getMethodName();
                                const stmts = method.getBody()?.getCfg()?.getStmts() ?? [];
                                for (const stmt of stmts) {
                                    const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt)
                                    if (invokeExpr) {
                                        const methodName = invokeExpr.getMethodSignature()?.getMethodSubSignature()?.getMethodName();
                                        if (!(methodName.startsWith('%'))) {
                                            ExtendmethodNameArray.push(methodName);
                                        }
                                    }
                                    if (stmt instanceof ArkAssignStmt) {
                                        const text = stmt.getOriginalText() ?? '';
                                        const rightOp = stmt.getRightOp() as unknown as stmtRightOp;
                                        const baseName = rightOp.base?.name;
                                        const fieldName = rightOp.fieldSignature?.getFieldName();
                                        const Methods = Class.getMethods();
                                        for (const ArkMethod of Methods) {
                                            const methodName = ArkMethod.getSignature().getMethodSubSignature().getMethodName();
                                            if (methodName == extendMethodName) {
                                                continue;
                                            }
                                            if (ExtendmethodNameArray.includes(baseName) && methodName == fieldName) {
                                                this.addIssueReport(file, stmt, text, baseName);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                    }
                    if (ClassName.startsWith('%')) {
                        const defaultMethod = Class.getDefaultArkMethod();
                        const stmts = defaultMethod?.getBody()?.getLocals() ?? [];
                        for (const [key, value] of stmts.entries()) {
                            if (key == 'this') {
                                continue;
                            }
                            if (key.toString().startsWith('%')) {
                                const declaringStmt = value.getDeclaringStmt();
                                if (!(declaringStmt instanceof ArkAssignStmt)) {
                                    continue;
                                }
                                const text = declaringStmt.getOriginalText() ?? '';
                                const LeftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                const RightOp = declaringStmt.getRightOp() as unknown as LeftOp;
                                const rightOpName = RightOp.name;
                                const usedStmts = LeftOp.usedStmts;
                                let leftOpName;
                                if (usedStmts && usedStmts.length > 0) {
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const leftOp = usedStmt.getLeftOp() as unknown as LeftOp;
                                        const constFlag = leftOp.constFlag;
                                        leftOpName = leftOp.name;
                                        if (constFlag == true && leftOpName && rightOpName && classes.length == 1) {
                                            this.addIssueReport(file, declaringStmt, text, leftOpName);
                                        }
                                    }
                                }
                                const RigOp = declaringStmt.getRightOp() as unknown as rightOpType;
                                const fieldName = RigOp.fieldSignature?.getFieldName();
                                const staticFlag = RigOp.fieldSignature?.isStatic();
                                const DeclaringSignature = RigOp.fieldSignature?.getDeclaringSignature() as unknown as declaringSignature;
                                const className = DeclaringSignature?.className;
                                if (text.includes(fieldName) && text.includes(className) && staticFlag == true) {
                                    if (options.ignoreStatic == false) {
                                        this.addIssueReport(file, declaringStmt, text, text);
                                    }
                                }
                                const rightOp = declaringStmt.getRightOp() as unknown as stmtRightOp;
                                if (rightOp.base && rightOp.fieldSignature) {
                                    const fieldSignature = rightOp?.fieldSignature;
                                    const fieldName = fieldSignature?.getFieldName();
                                    const DeclaringSignature = fieldSignature?.getDeclaringSignature() as unknown as declaringSignature;
                                    const className = DeclaringSignature?.className;
                                    const target = `${className}().${fieldName}`;
                                    const usedStmts = value.getUsedStmts();
                                    let FieldName;
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const rightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                        FieldName = rightOp?.fieldSignature?.getFieldName();
                                    }
                                    const target1 = `${className}.prototype.${FieldName}`;
                                    if (text.includes(target)) {
                                        this.addIssueReport(file, declaringStmt, text, text);
                                    }
                                    if (text.includes(target1) && className !== '') {
                                        this.addIssueReport(file, declaringStmt, text, target1);
                                    }
                                    const target2 = `${className}.prototype`;
                                    if (text.includes(target2) && className !== '' && leftOpName) {
                                        this.addIssueReport(file, declaringStmt, text, leftOpName);
                                    }
                                    const target3 = 'Promise.all';
                                    if (text.includes(target3) && className !== '') {
                                        this.addIssueReport(file, declaringStmt, text, target3);
                                    }
                                } else {
                                    const text = declaringStmt.getOriginalText() ?? '';
                                    const leftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                    const rightOp = declaringStmt.getRightOp() as unknown as LeftOp;
                                    const FieldName = rightOp.type?.classSignature?.getClassName();
                                    const usedStmts = leftOp.usedStmts;
                                    const arkClass = declaringStmt.getCfg()?.getDeclaringMethod()?.getDeclaringArkClass();
                                    const classes = arkClass.getDeclaringArkFile()?.getClasses();
                                    for (const [key, value] of classes.entries()) {
                                        const className = value.getSignature().getClassName();
                                        const code = value.getCode() ?? '';
                                        if (className.startsWith('%')) {
                                            continue;
                                        }
                                        if (code.includes('=>')) {
                                            continue;
                                        }
                                        const fields = value.getFields();
                                        for (const [key, value] of fields.entries()) {
                                            const fieldSignature = value.getSignature();
                                            const fieldName = fieldSignature.getFieldName();
                                            const declaringSignature = fieldSignature.getDeclaringSignature() as unknown as declaringSignature;
                                            const ClassName = declaringSignature.className;
                                            const target = `new ${className}().${fieldName}`;
                                            if (usedStmts && usedStmts.length > 0) {
                                                for (const usedStmt of usedStmts) {
                                                    if (!(usedStmt instanceof ArkAssignStmt)) {
                                                        continue;
                                                    }
                                                    const funName = usedStmt?.getLeftOp().toString();
                                                    if (funName == fieldName && FieldName == ClassName) {
                                                        this.addIssueReport(file, declaringStmt, text, funName);
                                                    }
                                                }
                                            }
                                            if (text.includes(target)) {
                                                this.addIssueReport(file, declaringStmt, text, target);
                                            }
                                        }
                                    }
                                }
                            } else {
                                const declaringStmt = value.getDeclaringStmt();
                                const text = declaringStmt?.getOriginalText() ?? '';
                                const target = 'Promise.all'
                                if (text.includes(target) && declaringStmt) {
                                    this.addIssueReport(file, declaringStmt, text, target);
                                }
                            }
                            const type = value.getType() as unknown as stmtType;
                            const typeClassSignature = type.classSignature;
                            if (typeClassSignature) {
                                const name = value.getName();
                                if (objectNameArray.includes(name)) {
                                    const usedStmts = value.getUsedStmts() ?? [];
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const leftOp = usedStmt.getLeftOp() as unknown as LeftOp;
                                        const leftOpName = leftOp.name;
                                        const rightOp = usedStmt.getRightOp() as unknown as LeftOp;
                                        const rightValue = rightOp.name;
                                        const LUsedStmts = leftOp.usedStmts ?? [];
                                        if (objectNameArray.includes(rightValue) && leftOpName.startsWith('%')) {
                                            for (const LUsedStmt of LUsedStmts) {
                                                const LeftOp = LUsedStmt.getLeftOp() as unknown as LeftOp;
                                                const LeftOpName = LeftOp.name;
                                                if (objectMethodNameArray.includes(LeftOpName)) {
                                                    if (category == 5) {
                                                        const methods = arkClass.getMethods();
                                                        for (const method of methods) {
                                                            let hasThisVoid: boolean = false;
                                                            const text = LUsedStmt.getOriginalText() ?? '';
                                                            const declareClass = method.getDeclaringArkClass()
                                                            const arkFile = declareClass.getDeclaringArkFile();
                                                            const methodSignature = method.getSignature();
                                                            const methodSubSignature = methodSignature.getMethodSubSignature();
                                                            const parameters = methodSubSignature.getParameters() ?? [];
                                                            if (parameters.length == 0) {
                                                                this.addIssueReport(arkFile, LUsedStmt, text, LeftOpName);
                                                            } else {
                                                                for (const parameter of parameters) {
                                                                    const parameterName = parameter.getName();
                                                                    const parameterType = parameter.getType();
                                                                    if (parameterName == 'this' && parameterType.toString() == 'void') {
                                                                        hasThisVoid = true;
                                                                    }
                                                                }
                                                                if (hasThisVoid == true) {
                                                                    continue;
                                                                }
                                                                this.addIssueReport(arkFile, LUsedStmt, text, LeftOpName);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                const className = typeClassSignature.getClassName();
                                if (classNameArray.includes(className)) {
                                    const usedStmts = value.getUsedStmts() ?? [];
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const text = usedStmt.getOriginalText() ?? '';
                                        const stmtRightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                        const filedSignature = stmtRightOp.fieldSignature;
                                        if (filedSignature) {
                                            const stmtFiledName = filedSignature.getFieldName();
                                            if (methodNameArray.includes(stmtFiledName)) {
                                                for (const method of methods) {
                                                    const declareClass = method.getDeclaringArkClass()
                                                    const arkFile = declareClass.getDeclaringArkFile();
                                                    const methodSignature = method.getSignature();
                                                    const MethodSubSignature = methodSignature.getMethodSubSignature();
                                                    const parameters = MethodSubSignature.getParameters() ?? [];
                                                    if (parameters.length > 0) {
                                                        for (const parameter of parameters) {
                                                            const parameterName = parameter.getName();
                                                            const parameterType = parameter.getType();
                                                            if (parameterName == 'this' && parameterType.toString() == 'void') {
                                                                continue;
                                                            }
                                                            else if (this.bindRegExp(text, `${className}`)) {
                                                                continue;
                                                            }
                                                            else {
                                                                const rightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                                                const className = rightOp?.base?.name;
                                                                const fieldName = rightOp?.fieldSignature?.getFieldName();
                                                                const target = `${className}.${fieldName}`;
                                                                if (text.includes(target)) {
                                                                    this.addIssueReport(arkFile, usedStmt, text, target);
                                                                }
                                                            }
                                                        }
                                                    } else {
                                                        if (this.bindRegExp(text, `${className}`)) {
                                                            continue;
                                                        }
                                                        else {
                                                            const rightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                                            const className = rightOp?.base?.name;
                                                            const fieldName = rightOp?.fieldSignature?.getFieldName();
                                                            const target = `${className}.${fieldName}`;
                                                            if (text.includes(target)) {
                                                                this.addIssueReport(arkFile, usedStmt, text, target);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        } else {
                                            const leftOp = usedStmt.getLeftOp() as unknown as LeftOp;
                                            const LUsedStmts = leftOp.usedStmts ?? [];
                                            if (leftOp?.toString().startsWith('%')) {
                                                for (const LUsedStmt of LUsedStmts) {
                                                    const leftOp = LUsedStmt.getLeftOp();
                                                    const leftOpName = leftOp.toString();
                                                    if (methodNameArray.includes(leftOpName)) {
                                                        for (const method of methods) {
                                                            const declareClass = method.getDeclaringArkClass()
                                                            const arkFile = declareClass.getDeclaringArkFile();
                                                            this.addIssueReport(arkFile, LUsedStmt, text, leftOpName);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            } else {
                                if (options.ignoreStatic == true) {
                                    for (const method of methods) {
                                        const declareClass = method.getDeclaringArkClass();
                                        const arkFile = declareClass.getDeclaringArkFile();
                                        const methodSignature = method.getSignature();
                                        const methodSubSignature = methodSignature.getMethodSubSignature();
                                        const staticFlag = methodSubSignature.isStatic();
                                        if (staticFlag == true) {
                                            continue;
                                        }
                                        const declaringStmt = value.getDeclaringStmt();
                                        if (!(declaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const rightOp = declaringStmt.getRightOp() as unknown as stmtRightOp;
                                        const leftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                        const rightOpDeclaringStmt = rightOp?.base?.declaringStmt ?? [];
                                        if (!(rightOpDeclaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const Rright = rightOpDeclaringStmt.getRightOp();
                                        const text = rightOpDeclaringStmt.getOriginalText() ?? '';
                                        if (classNameArray.includes(Rright.toString()) && methodNameArray.includes(leftOp.toString())) {
                                            this.addIssueReport(arkFile, rightOpDeclaringStmt, text, leftOp.toString());
                                        }
                                    }
                                } else {
                                    for (const method of methods) {
                                        const declareClass = method.getDeclaringArkClass();
                                        const arkFile = declareClass.getDeclaringArkFile();
                                        const Value = value as unknown as LeftOp;
                                        const declaringStmt = Value.declaringStmt ?? [];
                                        if (!(declaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const text = declaringStmt.getOriginalText() ?? '';
                                        const rightOp = declaringStmt.getRightOp() as unknown as fieldSignature;
                                        const leftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                        const declaringSignature = rightOp?.fieldSignature?.getDeclaringSignature() as unknown as declaringSignature;
                                        const className = declaringSignature?.className;
                                        if (classNameArray.includes(className) && methodNameArray.includes(leftOp.toString())) {
                                            this.addIssueReport(arkFile, declaringStmt, text, leftOp.toString())
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                for (const Class of classes) {
                    const ClassSignature = Class.getSignature();
                    const ClassName = ClassSignature.getClassName();
                    if (ClassName.startsWith('%')) {
                        const defaultMethod = Class.getDefaultArkMethod();
                        const stmts = defaultMethod?.getBody()?.getLocals() ?? [];
                        for (const [key, value] of stmts.entries()) {
                            if (key.toString().startsWith('%') || key == 'this') {
                                continue;
                            }
                            const type = value.getType() as unknown as stmtType;
                            const typeClassSignature = type.classSignature;
                            if (typeClassSignature) {
                                const name = value.getName();
                                if (objectNameArray.includes(name)) {
                                    const usedStmts = value.getUsedStmts() ?? [];
                                    for (const usedStmt of usedStmts) {
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const leftOp = usedStmt.getLeftOp() as unknown as LeftOp;
                                        const leftOpName = leftOp.name;
                                        const rightOp = usedStmt.getRightOp() as unknown as LeftOp;
                                        const rightValue = rightOp.name;
                                        const LUsedStmts = leftOp.usedStmts ?? [];
                                        if (objectNameArray.includes(rightValue) && leftOpName.startsWith('%')) {
                                            for (const LUsedStmt of LUsedStmts) {
                                                const LeftOp = LUsedStmt.getLeftOp() as unknown as LeftOp;
                                                const RightOp = LUsedStmt.getRightOp() as unknown as stmtRightOp;
                                                const LeftOpName = LeftOp.name;
                                                const text = LUsedStmt.getOriginalText() ?? '';
                                                if (objectMethodNameArray.includes(LeftOpName)) {
                                                    if (category == 5) {
                                                        const methods = arkClass.getMethods();
                                                        for (const method of methods) {
                                                            let hasThisVoid: boolean = false;
                                                            const text = LUsedStmt.getOriginalText() ?? '';
                                                            const declareClass = method.getDeclaringArkClass()
                                                            const arkFile = declareClass.getDeclaringArkFile();
                                                            const methodSignature = method.getSignature();
                                                            const methodSubSignature = methodSignature.getMethodSubSignature();
                                                            const parameters = methodSubSignature.getParameters() ?? [];
                                                            if (parameters?.length == 0) {
                                                                if (LUsedStmts?.length > 1) {
                                                                    const leftOp = LUsedStmts[0].getLeftOp() as unknown as LeftOp;
                                                                    const leftOpName = leftOp.name;
                                                                    this.addIssueReport(arkFile, LUsedStmt, text, leftOpName);
                                                                } else {
                                                                    this.addIssueReport(arkFile, LUsedStmt, text, LeftOpName);
                                                                }
                                                            } else {
                                                                for (const parameter of parameters) {
                                                                    const parameterName = parameter.getName();
                                                                    const parameterType = parameter.getType();
                                                                    if (parameterName == 'this' && parameterType.toString() == 'void') {
                                                                        hasThisVoid = true;
                                                                    }
                                                                }
                                                                if (hasThisVoid == true) {
                                                                    continue;
                                                                }
                                                                this.addIssueReport(arkFile, LUsedStmt, text, LeftOpName);
                                                            }
                                                        }
                                                    }
                                                } else {
                                                    const fieldName = RightOp.fieldSignature.getFieldName();
                                                    if (fieldName) {
                                                        const methods = arkClass.getMethods();
                                                        for (const method of methods) {
                                                            const declareClass = method.getDeclaringArkClass()
                                                            const arkFile = declareClass.getDeclaringArkFile();
                                                            if (text.includes(',') && text.includes(fieldName)) {
                                                                continue;
                                                            }
                                                            this.addIssueReport(arkFile, LUsedStmt, text, fieldName);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                const className = typeClassSignature.getClassName();
                                if (classNameArray.includes(className)) {
                                    const usedStmts = value.getUsedStmts() ?? [];
                                    for (const usedStmt of usedStmts) {
                                        const text = usedStmt.getOriginalText() ?? '';
                                        if (!(usedStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const stmtRightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                        const filedSignature = stmtRightOp.fieldSignature;
                                        if (filedSignature) {
                                            const stmtFiledName = filedSignature.getFieldName();
                                            if (fieldNameArray.includes(stmtFiledName)) {
                                                continue;
                                            }
                                            if (methodNameArray.includes(stmtFiledName)) {
                                                for (const method of methods) {
                                                    const declareClass = method.getDeclaringArkClass()
                                                    const arkFile = declareClass.getDeclaringArkFile();
                                                    const methodSignature = method.getSignature();
                                                    const MethodSubSignature = methodSignature.getMethodSubSignature();
                                                    const parameters = MethodSubSignature.getParameters() ?? [];
                                                    const methodName = method.getName();
                                                    if (methodName.startsWith('%')) {
                                                        continue;
                                                    }
                                                    if (parameters.length > 0) {
                                                        for (const parameter of parameters) {
                                                            const parameterName = parameter.getName();
                                                            const parameterType = parameter.getType();
                                                            if (parameterName == 'this' && parameterType.toString() == 'void') {
                                                                continue;
                                                            }
                                                            else if (this.bindRegExp(text, `${className}`)) {
                                                                continue;
                                                            }
                                                            else {
                                                                const rightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                                                const className = rightOp?.base?.name;
                                                                const fieldName = rightOp?.fieldSignature?.getFieldName();
                                                                const target = `${className}.${fieldName}`;
                                                                if (text.includes(target)) {
                                                                    this.addIssueReport(arkFile, usedStmt, text, target);
                                                                }
                                                            }
                                                        }
                                                    } else {
                                                        if (this.bindRegExp(text, `${className}`)) {
                                                            continue;
                                                        } else {
                                                            const rightOp = usedStmt.getRightOp() as unknown as stmtRightOp;
                                                            const className = rightOp?.base?.name;
                                                            const fieldName = rightOp?.fieldSignature?.getFieldName();
                                                            const target = `${className}.${fieldName}`;
                                                            if (text.includes(target)) {
                                                                this.addIssueReport(arkFile, usedStmt, text, target);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        } else {
                                            const leftOp = usedStmt.getLeftOp() as unknown as LeftOp;
                                            const LUsedStmts = leftOp.usedStmts ?? [];
                                            if (leftOp.toString().startsWith('%')) {
                                                for (const LUsedStmt of LUsedStmts) {
                                                    const leftOp = LUsedStmt.getLeftOp();
                                                    const leftOpName = leftOp.toString();
                                                    if (fieldNameArray.includes(leftOpName)) {
                                                        continue;
                                                    }
                                                    if (methodNameArray.includes(leftOpName)) {
                                                        for (const method of methods) {
                                                            const declareClass = method.getDeclaringArkClass()
                                                            const arkFile = declareClass.getDeclaringArkFile();
                                                            this.addIssueReport(arkFile, LUsedStmt, text, leftOpName)
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            } else {
                                if (options.ignoreStatic == true) {
                                    for (const method of methods) {
                                        const declareClass = method.getDeclaringArkClass();
                                        const arkFile = declareClass.getDeclaringArkFile();
                                        const methodSignature = method.getSignature();
                                        const methodSubSignature = methodSignature.getMethodSubSignature();
                                        const staticFlag = methodSubSignature.isStatic();
                                        const methodName = methodSubSignature.getMethodName();
                                        if (staticFlag == true) {
                                            continue;
                                        }
                                        if (staticFlag == false && methodName.startsWith('%')) {
                                            continue;
                                        }
                                        const declaringStmt = value.getDeclaringStmt();
                                        if (!(declaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const rightOp = declaringStmt.getRightOp() as unknown as stmtRightOp;
                                        const leftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                        const rightOpDeclaringStmt = rightOp?.base?.declaringStmt ?? [];
                                        if (!(rightOpDeclaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const Rright = rightOpDeclaringStmt.getRightOp();
                                        const text = rightOpDeclaringStmt.getOriginalText() ?? '';
                                        if (classNameArray.includes(Rright.toString()) && methodNameArray.includes(leftOp.toString())) {
                                            this.addIssueReport(arkFile, rightOpDeclaringStmt, text, leftOp.toString());
                                        }
                                    }
                                } else {
                                    for (const method of methods) {
                                        const declareClass = method.getDeclaringArkClass();
                                        const arkFile = declareClass.getDeclaringArkFile();
                                        const Value = value as unknown as LeftOp;
                                        const declaringStmt = Value.declaringStmt ?? [];
                                        if (!(declaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const rightOp = declaringStmt.getRightOp() as unknown as stmtRightOp;
                                        const leftOp = declaringStmt.getLeftOp() as unknown as LeftOp;
                                        const rightOpDeclaringStmt = rightOp?.base?.declaringStmt ?? [];
                                        if (!(rightOpDeclaringStmt instanceof ArkAssignStmt)) {
                                            continue;
                                        }
                                        const Rright = rightOpDeclaringStmt.getRightOp();
                                        const text = rightOpDeclaringStmt.getOriginalText() ?? '';
                                        if (classNameArray.includes(Rright.toString()) && methodNameArray.includes(leftOp.toString())) {
                                            this.addIssueReport(arkFile, rightOpDeclaringStmt, text, leftOp.toString())
                                        }
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
    }
    private bindRegExp(text: string, bindParameter: string) {
        // 动态构建正则表达式，将 bindParameter 插入到捕获组中
        const regex = new RegExp(`\\bconst\\s+\\w+\\s*=\\s*\\w+\\.\\w+\\.bind\\((${bindParameter})\\)\\s*`);
        return regex.test(text);
    }
    private addIssueReport(arkFile: ArkFile, stmt: Stmt, name: string, targetName: string) {
        const severity = this.rule.alert ?? this.metaData.severity;
        const warnInfo = this.getLineAndColumn(arkFile, stmt, name, targetName);
        if (warnInfo) {
            this.metaData.description = this.metaData.description.padEnd(130);
            const fixKey = warnInfo.line + '%' + warnInfo.startCol + '%' + this.rule.ruleId;
            const mergeKey = warnInfo.filePath + '%' + fixKey + '%' + this.metaData.description + this.rule.ruleId;
            const defect = new Defects(warnInfo.line ?? 0, warnInfo.startCol, this.metaData.description, severity, this.rule.ruleId,
                mergeKey, this.metaData.ruleDocPath, true, false, false, fixKey);
            this.issues.push(new IssueReport(defect, undefined));
            RuleListUtil.push(defect);
        } else {
            logger.debug('No valid line number found for issue report.');
        }
    }
    private getLineAndColumn(arkfile: ArkFile, stmt: Stmt, name: string, targetName: string) {
        if (arkfile) {
            const code = arkfile.getCode() ?? '';
            const lines = code.split('\r\n');
            const originPosition = stmt.getOriginPositionInfo();
            const lineNumber = originPosition.getLineNo();
            for (const line of lines) {
                const lineIndex = line.indexOf(name);
                if (lineIndex !== -1) {
                    if (line.startsWith('//')) {
                        continue;
                    }
                    const targetIndex = line.indexOf(targetName);
                    if (targetIndex !== -1) {
                        const startCol = targetIndex + 1;
                        const originPath = arkfile.getFilePath();
                        return { line: lineNumber, startCol, filePath: originPath }
                    }
                }
            }
        } else {
            logger.debug('originStmt or arkFile is null');
        }
        return null;
    }
}